package main

import (
	"fmt"
	"sort"
)

// ==========映射==========
// 1、map是key-value数据结构, 又称为字段或者关联数组; 类似其它编程语言的集合.
// 2、map是无序的, 类型于Python的字典, key不可以重复, value可以重复

func modifyMap(mapX *map[int]int) {
	(*mapX)[10] = 900
}

func modifyUser(user map[string]map[string]string, name string) {
	//判断user中是否有name
	//vak, ok := user[name]
	if user[name] != nil {
		// 有这个name
		user[name]["pwd"] = "8888888"
	} else {
		// 没有这个用户
		user[name] = make(map[string]string)
		user[name]["pwd"] = "00000000"
		user[name]["nickname"] = "what"
	}
}

// 定义一个学生结构体
type Stu struct {
	name    string
	age     int
	address string
}

func main() {
	// 基本语法
	/*
		var 变量名 map[keyType]valueType

		• key的类型
		  - bool
		  - 数字(int、float)
		  - string
		  - 指针
		  - chan
		  还可以是只包含前面几个类型的接口、结构体、数组, 通常为int、string
		  注意: slice、map还有function不可以, 因为这几个没法用==来判断

		• value的类型
			  - bool
			  - 数字(int、float)
			  - string
			  - 指针
			  - chan
			  通常为int、float、string、map、struct
	*/

	/*
		map声明举例
		var a map[string]string
		var a map[string]int
		var a map[int]string
		var a map[string]map[string]string
		声明不会分配内存, 初始化需要make, 分配内存后才能赋值和使用
	*/

	// 方式1: 先声明后make
	var a map[string]string
	fmt.Println(a) // map[]
	// 在使用map前, 需要先make, make的作用就是给map分配数据空间
	a = make(map[string]string, 2) // 可以放2对key-value, 可动态增加, size也可以不写
	a["name1"] = "max"
	a["name2"] = "kitty"
	a["name1"] = "Max" // 覆盖max
	a["name3"] = "js"
	fmt.Println(a) // map[name1:Max name2:kitty name3:js]

	// 方式2: 声明的时候就make
	var b map[string]string = make(map[string]string)
	b["name"] = "monster"
	fmt.Println(b) // map[name:monster]

	// 方式3: 声明的时候直接赋值
	var c = map[string]int{"age": 10, "salary": 1000}
	fmt.Println(c) // map[age:10 salary:1000]

	// 案例, 存放学生姓名和性别
	student := make(map[int]map[string]string)
	student[1] = make(map[string]string)
	student[1]["姓名"] = "陆佳伟"
	student[1]["性别"] = "男"
	student[2] = make(map[string]string)
	student[2]["姓名"] = "陈信宏"
	student[2]["性别"] = "男"
	fmt.Println(student) // map[1:map[姓名:陆佳伟 性别:男] 2:map[姓名:陈信宏 性别:男]]

	// map的增删改查
	// 增加
	b["gender"] = "女"
	// 更新
	b["gender"] = "未知"
	// 删除, delete是一个内置函数, 如果key存在, 则删除该key-value键值对, key不存在, 不操作也不报错
	delete(b, "gender")
	delete(b, "address")
	// 若要一次性删除所有的key
	// 1、for-range遍历所有的key, 逐一删除
	// 2、直接make一个新的空间(推荐)
	b = make(map[string]string)
	fmt.Println(b) // map[]
	// 查找, 如果key存在, 则findRes返回true, 反之false
	val, findRes := c["age"]
	fmt.Println(val, findRes) // 10 true
	val, findRes = c["age1"]
	fmt.Println(val, findRes) // 0 false

	// map的遍历
	city := make(map[string]string)
	city["no1"] = "上海"
	city["no2"] = "北京"
	city["no3"] = "深圳"
	for key, value := range city {
		fmt.Println(key, value)
	}
	for k1, v1 := range student {
		for k2, v2 := range v1 {
			fmt.Println(k1, k2, v2)
		}
	}

	// map的长度, 用len来统计有几对key-value
	fmt.Println(len(student))

	// map切片
	var monster = make([]map[string]string, 2)
	fmt.Println(monster) // [map[] map[]]
	// 增加第一个妖怪
	if monster[0] == nil {
		monster[0] = make(map[string]string, 2)
		monster[0]["name"] = "牛魔王"
		monster[0]["age"] = "500"
	}
	// 增加第二个妖怪
	if monster[1] == nil {
		monster[1] = make(map[string]string, 2)
		monster[1]["name"] = "玉兔精"
		monster[1]["age"] = "400"
	}
	// 下面写法越界
	//if monster[2] == nil {
	//	monster[2] = make(map[string]string, 2)
	//	monster[2]["name"] = "狐狸精"
	//	monster[2]["age"] = "300"
	//}
	// 用切片的append动态增加
	newMonster := map[string]string{
		"name": "火云邪神",
		"age":  "200", // 若最后一个键值对和右}不在同一行, 最后一个键值对的值后面必须加,
	}
	monster = append(monster, newMonster)
	fmt.Println(monster)

	// map排序
	// golang中没有一个专门的方法针对map的key进行排序
	// golang中的map默认是无序的, 并不是按照添加顺序来排序
	// golang中map的排序, 是先将key进行排序, 然后根据key值遍历输出即可
	// 1、先将map的key放入到切片中
	// 2、对切片排序
	// 3、遍历切片, 然后根据key来输出map的值
	map1 := make(map[int]int)
	map1[10] = 100
	map1[1] = 13
	map1[4] = 56
	map1[8] = 90
	var keys []int
	for k := range map1 {
		keys = append(keys, k)
	}
	sort.Ints(keys)
	fmt.Println(keys)
	for _, k := range keys {
		fmt.Println(k, map1[k])
	}

	// map使用细节
	/*
		1、map是引用类型, 遵守引用类型传递的机制, 在一个函数接收map, 修改后, 会直接修改原来的map
		2、map的容量达到后, 再向map增加元素, 会自动扩容, 并不会发生panic
		3、map的value也经常使用struct类型, 更适合管理复杂的数据
	*/
	// map细节1案例
	m1 := map[int]int{
		10: 100,
		20: 200,
	}
	modifyMap(&m1)
	fmt.Println(m1) // map[10:900 20:200]
	// map细节3案例
	stu := make(map[string]Stu)
	// 创建2个学生
	stu1 := Stu{"Tome", 25, "北京"}
	stu2 := Stu{"Mary", 22, "上海"}
	stu["no1"] = stu1
	stu["no2"] = stu2
	fmt.Println(stu) // map[no1:{Tome 25 北京} no2:{Mary 22 上海}]
	for k, v := range stu {
		fmt.Println(k, v.name, v.age, v.address)
	}
	/*
		no1 Tome 25 北京
		no2 Mary 22 上海
	*/

	fmt.Println(student[10] == nil) // true
	v, ok := student[10]
	fmt.Println(v, ok) // map[] false

	// map的练习
	user := make(map[string]map[string]string)
	user["陆佳伟"] = make(map[string]string)
	user["陆佳伟"]["pwd"] = "123456"
	user["陆佳伟"]["nickname"] = "max"
	modifyUser(user, "陈信宏")
	fmt.Println(user) // map[陆佳伟:map[nickname:max pwd:123456] 陈信宏:map[nickname:what pwd:00000000]]
	modifyUser(user, "陆佳伟")
	fmt.Println(user) // map[陆佳伟:map[nickname:max pwd:8888888] 陈信宏:map[nickname:what pwd:00000000]]
}
